package edu.ncsu.csc.ip.routing.dv;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;

/**
 * Class <code>distance_vector</code>
 * 
 * distance_vector to comply with project description
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public class distance_vector {

	HashMap<String, RouterLocal> nodesMap = new HashMap<String, RouterLocal>();
	public static final boolean DIRECTED_GRAPH = false;

	public static Comparator<String> NodeNameComparator = new Comparator<String>() {
		public int compare(String node1, String node2) {
			// ascending order
			return Integer.valueOf(node1).compareTo(Integer.valueOf(node2));
		}
	};

	public static Comparator<Node> NodeComparator = new Comparator<Node>() {
		public int compare(Node node1, Node node2) {
			// ascending order
			return Integer.valueOf(node1.getName()).compareTo(
					Integer.valueOf(node2.getName()));
		}
	};

	public static Comparator<RouterLocal> NodeNameRouterComparator = new Comparator<RouterLocal>() {
		public int compare(RouterLocal r1, RouterLocal r2) {
			// ascending order
			return Integer.valueOf(r1.localNode.getName()).compareTo(
					Integer.valueOf(r2.localNode.getName()));
		}
	};

	public static void main(String[] args) throws Exception {

		try {
			distance_vector dv = new distance_vector();
			dv.loadGraph(args[1]);

			String initialNode = args[0];
			String sourceNode = args[2];
			String destNode = args[3];

			RouterLocal[] routerArray = dv.nodesMap.values().toArray(
					new RouterLocal[dv.nodesMap.size()]);
			Arrays.sort(routerArray, NodeNameRouterComparator);

			dv.nodesMap.get(initialNode).initialize();
			dv.nodesMap.get(initialNode).start();
			for (RouterLocal r : routerArray) {
				if (r != dv.nodesMap.get(initialNode))
					r.initialize();
			}

			for (RouterLocal r : routerArray) {
				if (r != dv.nodesMap.get(initialNode))
					r.start();
			}

			int wait_time = 5000;
			System.out.println(" Waiting for network to converge.\n\n");
			
			Thread.sleep(wait_time);

			printResults(dv.nodesMap.get(sourceNode), dv.nodesMap.get(destNode));

			printResults(dv.nodesMap.get(destNode), dv.nodesMap.get(sourceNode));

		} catch (Exception e) {
			System.err
					.println("USAGE:\n java distance_vector <initial node> <file name> <source node> <dest node>\n Example:\njava distance_vector 1 ./network/node1.txt 1 10");
			e.printStackTrace();
		}
	}

	public static void printResults(RouterLocal sourceRouter,
			RouterLocal destinationRouter) {

		Node source = sourceRouter.localNode;
		Node destination = destinationRouter.localNode;

		System.out.println("Source: " + source);
		System.out.println("Destination: " + destination);
		System.out.println("Cost: "
				+ source.distanceVector.get(destination).weight);
		System.out.println("Thru Node: "
				+ source.distanceVector.get(destination).nextNode);
		System.out.println("Path: "
				+ sourceRouter.getLeastCostPathTo(destination));
		System.out.println();

		System.out.println("---------Routing Table of Node " + source
				+ "-------------");
		System.out.println("DestNode	ThruNode	Cost	Path");

		Node[] nodes = source.distanceVector.keySet().toArray(
				new Node[source.distanceVector.size()]);
		Arrays.sort(nodes, NodeComparator);

		for (Node v : nodes) {
			if (!v.equals(source))
				System.out.println(v + "		"
						+ source.distanceVector.get(v).nextNode + "		"
						+ source.distanceVector.get(v).weight + "	"
						+ sourceRouter.getLeastCostPathTo(v));
		}
		System.out.println("------------------------------------------\n\n");
	}

	public void loadGraph(String fileName) throws IOException {
		File file;
		BufferedReader fileReader;
		nodesMap = new HashMap<String, RouterLocal>();
		file = new File(fileName);
		fileReader = new BufferedReader(new FileReader(file));
		String line;

		int nodeCount = Integer.parseInt(fileReader.readLine());
		while ((line = fileReader.readLine()) != null) {

			String[] params = line.split(" ");
			double weight = Double.parseDouble(params[2]);
			if (!nodesMap.containsKey(params[0]))
				nodesMap.put(params[0], new RouterLocal(new Node(params[0])));
			if (!nodesMap.containsKey(params[1]))
				nodesMap.put(params[1], new RouterLocal(new Node(params[1])));

			nodesMap.get(params[0]).localNode.neighborsMap.put(
					nodesMap.get(params[1]).localNode,
					new RouteEntity(nodesMap.get(params[1]).localNode, weight));

			if (!DIRECTED_GRAPH)
				nodesMap.get(params[1]).localNode.neighborsMap.put(nodesMap
						.get(params[0]).localNode,
						new RouteEntity(nodesMap.get(params[0]).localNode,
								weight));
		}
		System.out.println(" Graph Loaded");
		fileReader.close();
		if (nodeCount != nodesMap.size())
			throw new RuntimeException(
					"Number of nodes doesn't match the first line of the input");

	}

}
